package com.liuqi.loggers.core.rolling;

import com.liuqi.loggers.core.Constants;
import com.liuqi.loggers.core.LifeCycleBase;
import com.liuqi.loggers.core.LoggerEvent;
import com.liuqi.loggers.core.util.RollingUtil;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 日期滚动策略
 * <p>
 *     支持按(月/日/时)滚动
 * </p>
 *
 * @author liuqi
 * @date 2024/11/5 17:06
 **/
public class TimeRollingPolicy extends LifeCycleBase implements RollingPolicy {

    /**
     * 日志滚动策略单位
     */
    public enum Unit {
        MONTH, DAY, HOUR
    }

    /**
     * 滚动前的日志文件保存目录
     */
    private File toDirectory;

    /**
     * 滚动单位
     */
    private Unit unit = Unit.DAY;

    /**
     * 间隔几个单位{@link #unit}滚动一次
     */
    private int interval = 1;

    /**
     * 最多保存多少分日志数据
     */
    private int maxHistory = 5;

    /**
     * 下一次滚动时间点, 当大于等于该时间点, 则触发滚动
     */
    private long nextCheck;

    @Override
    public boolean isTriggering(LoggerEvent event) {
        if (event.getTimestamp() > nextCheck) {
            computeNextCheck();
            return true;
        }
        return false;
    }

    @Override
    public void rollover(File activeFile) throws RolloverFailure {
        if (toDirectory == null) {
            toDirectory = activeFile.getParentFile();
        }

        String filename = RollingUtil.filename(activeFile);
        String suffix = RollingUtil.suffix(activeFile.getName());

        String dateStr = null;
        switch (unit) {
            case MONTH:
                dateStr = RollingUtil.preMonth(interval);
                break;
            case DAY:
                dateStr = RollingUtil.preDay(interval);
                break;
            case HOUR:
                dateStr = RollingUtil.preHour(interval);
                break;
        }

        // xxx.log ->  xxx_${dateStr}.log
        String newFileName = filename + "_" + dateStr + Constants.DOT + suffix;
        activeFile.renameTo(new File(toDirectory, newFileName));

        // 删除历史记录
        removeHistory(filename);
    }

    @Override
    public void start() {
        this.computeNextCheck();
    }

    /**
     * 计算下一个滚动点
     */
    void computeNextCheck() {
        switch (unit) {
            case MONTH:
                nextCheck = RollingUtil.nextMonth(interval); break;
            case DAY:
                nextCheck = RollingUtil.nextDay(interval); break;
            case HOUR:
                nextCheck = RollingUtil.nextHour(interval); break;
        }
    }

    void removeHistory(final String filename) {
        File[] files = toDirectory.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().contains(filename)
                        && !filename.equals(RollingUtil.filename(pathname));
            }
        });

        if (files.length > maxHistory) {
            List<File> fileList = Arrays.asList(files);
            Collections.sort(fileList, new Comparator<File>() {
                @Override
                public int compare(File f1, File f2) {
                    return f1.compareTo(f2);
                }
            });

            // remove history
            for (int i = 0; i < fileList.size() - maxHistory; i++, fileList.get(i).delete());
        }
    }

    public long getNextCheck() {
        return nextCheck;
    }

    public File getToDirectory() {
        return toDirectory;
    }

    public void setToDirectory(File toDirectory) {
        this.toDirectory = toDirectory;
    }

    public Unit getRollingUnit() {
        return unit;
    }

    public void setRollingUnit(Unit rollingUnit) {
        this.unit = rollingUnit;
    }

    public int getInterval() {
        return interval;
    }

    public void setInterval(int interval) {
        this.interval = interval;
    }

    public int getMaxHistory() {
        return maxHistory;
    }

    public void setMaxHistory(int maxHistory) {
        this.maxHistory = maxHistory;
    }
}
